<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2017/9/26
 * Time: 13:08
 */

/**
 * Class http
 * @User: yfl
 * @Time: 2017年9月26日 13:09:38 from http://www.jb51.net/article/51974.htm
 * @description:php发送get、post请求的6种方法简明总结,分别为使用file_get_contents 、fopen、fsockopen、curl来发送GET和POST请求
 */
class http
{
    /**
     * @User: yfl
     * @Time: 2017年9月26日 13:15:20
     * @description:用file_get_contents 以get方式获取内容
     */
    public static function fileGetContents($url)
    {
        if (!self::checkUrlIsGet($url)) return '不合法的请地址';
        if (function_exists('file_get_contents')) {
            if (static::checkUrlIsGet($url)) {
                $html = file_get_contents($url);
                return $html;
            }
        }
    }

    /**
     * @param $url
     * @return string
     * @User: yfl
     * @Time: 2017年9月26日 13:15:05
     * @description:用fopen打开url, 以get方式获取内容：
     */
    public static function getUrlByFOpen($url)
    {
        if (!self::checkUrlIsGet($url)) return '不合法的请地址';
        if (function_exists('fopen')) {
            $fp = fopen($url, 'r');
            stream_get_meta_data($fp);
            $result = '';
            while (!feof($fp)) {
                $result .= fgets($fp, 1024);
            }
            fclose($fp);
            return $result;
        }

    }

    /**
     * @param $url
     * @param array $data
     * @return bool|string
     * @User: yfl
     * @Time: 2017年9月26日 14:15:20
     * @description:用file_get_contents函数,以post方式获取url
     */
    public static function fileGetContentsByPost($url, array $data)
    {
        if (!self::checkUrlIsGet($url)) return '不合法的请地址';
        if (function_exists('file_get_contents')) {
            $data = http_build_query($data);

            //规定文件句柄的环境
            $opts = array(
                'http' => array(
                    'method' => 'POST',
                    'header' => 'Content-type: application/x-www-form-urlencodedrn' . 'Content-Length:' . strlen($data) . "\r\n",
                    'content' => $data
                )
            );
            $context = stream_context_create($opts);  //创建并返回一个文本数据流并应用各种选项，可用于fopen(),file_get_contents()等过程的超时设置、代理服务器、请求方式、头信息设置的特殊过程
            //和 file() 一样，不同的是 file_get_contents() 把文件读入一个字符串。
            //必需。规定要读取的文件。
            //可选。如果也想在 include_path 中搜寻文件的话，可以将该参数设为 "1"。
            //可选。规定文件句柄的环境。
            $html = file_get_contents($url, false, $context);
            return $html;
        }
    }

    /**
     * @param $url
     * @param bool $Cookie
     * @return bool|string
     * @User: yfl
     * @Time: 2017年9月26日 13:56:01
     * @description:用fsockopen函数打开url，以get方式获取完整的数据，包括header和body,fsockopen需要 PHP.ini 中 allow_url_fopen 选项开启
     */


    private function fsockOpenGetUrl($url, $Cookie = false)
    {
        if (!self::checkUrlIsGet($url)) return '不合法的请地址';
        $url = parse_url($url);
//        var_dump($url);//array(2) { ["scheme"]=> string(4) "http" ["host"]=> string(13) "www.baidu.com" }
        $path = isset($url['path']) ? $url['path'] : '';
        $query = isset($url['query']) ? $url['query'] : '';
        $query = $path . '?' . $query;
        //fsockopen — 打开一个网络连接或者一个Unix套接字连接
        //如果安装了OpenSSL，那么你也许应该在你的主机名地址前面添加访问协议ssl://或者是tls://，从而可以使用基于TCP/IP协议的SSL或者TLS的客户端连接到远程主机。
        //端口号。如果对该参数传一个-1，则表示不使用端口，例如unix://。
        //错误信息将以字符串的信息返回。
        //设置连接的时限，单位为秒。
        $host = $url['host'];
        $fp = fsockopen($host, isset($url['port']) ? $url['port'] : 80, $error, $errStr, 30);
        if ($fp === false) {
            echo '调用失败';
            echo "$errStr ($error)\n";
            return false;
        } else {
            $request = "GET $query HTTP/1.1\r\n";
            $request .= "Host: $host\r\n";
            $request .= "Connection: Close\r\n\r\n";
            if ($Cookie) $request .= "Cookie:" . $Cookie;
            $request .= "\r\n";
            fwrite($fp, $request);
            $result = '';
            while (!feof($fp)) {
                $result .= fgets($fp, 1024);
            }
            fclose($fp);
            return $result;
        }
    }

    //用fsockopen函数打开url，以POST方式获取完整的数据，包括header和body  referrer 属性可返回载入当前文档的文档的 URL。
    public static function fsockOpenPostUrl($url, $data, $Cookie, $referrer = '')
    {
        if (!self::checkUrlIsGet($url)) return '不合法的请地址';
        $urlInfoArr = parse_url($url);
        //// Building referrer
        if ($referrer == '') $referrer = '111';  //// if not given use this script as referrer
        //making string from $data
        $values = array();
        foreach ($data as $key => $val) {
            $values[] = "$key=" . urlencode($val);
        }
        $dataString = implode('&', $values);
        $port = isset($urlInfoArr['port']) ? $urlInfoArr['port'] : 80;
        $request = "POST" . $urlInfoArr['path'] . "HTTP/1.1.\r\n";
        $request .= "Host:" . $urlInfoArr['host'] . "\r\n";
        $request .= "Referer:" . $referrer . "\r\n";
        $request .= "Content-type: application/x-www-form-urlencodedn" . "\r\n";
        $request .= "Content-length: " . strlen($dataString) . "\r\n";
        $request .= "Connection: Close\r\n\r\n";
        if ($Cookie) $request .= "Cookie:" . $Cookie . "\r\n";
        $request .= "\r\n";
        $request .= $dataString . "\r\n";
        $fp = fsockopen($urlInfoArr['host'], $urlInfoArr['port'], $error, $errStr, 30);
        if ($fp === false) {
            echo '调用失败';
            echo "$errStr ($error)\n";
            return false;
        } else {
            fputs($fp, $request);
            $result = '';
            while (!feof($fp)) {
                $result .= fgets($fp, 1024);
            }
            fclose($fp);
            return $result;
        }
    }

    /**
     * @param $url
     * @param bool $Cookie
     * @return bool|string
     * @User: yfl
     * @Time: 2017年9月26日 14:34:42
     * @description:获取url的html部分，去掉header
     */
    public function getUrlHtmlOutHeader($url, $Cookie = false)
    {
        if (!self::checkUrlIsGet($url)) return '不合法的请地址';
        $resData = $this->fsockOpenGetUrl($url, $Cookie);

        if ($resData !== false) {
            $body = stristr($resData, "\r\n");
//             strrchr() 显示最后一次找到，要查找的字符串，以及后面的字符串。
            $body = substr($body, 4, strlen($body));
            return $body;
        } else {
            return false;
        }
    }

    /**
     * @param $url
     * @param $postData
     * @return mixed
     * @User: yfl
     * @Time: 2017年9月26日 15:47:33
     * @description:使用curl库，使用curl库之前，可能需要查看一下php.ini是否已经打开了curl扩展
     */
    public static function curlRequest($url, $postData = false)
    {
        if (!self::checkUrlIsGet($url)) return '不合法的请地址';
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        if ($postData !== false) {
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        }
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $result = curl_exec($ch);
        curl_close($ch);
        return $result;
    }

    private function get_html($url)
    {

        $ch = curl_init();

        $timeout = 10;

        curl_setopt($ch, CURLOPT_URL, $url);

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

        curl_setopt($ch, CURLOPT_ENCODING, 'gzip');

        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36');

        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);

        $html = curl_exec($ch);

        return $html;

    }

    /**
     * @param $array_url
     * @param int $wait_usec
     * @return array|bool
     * @User: yfl
     * @Time:
     * @description://第一步：调用curl_multi_init
     * //第二步：循环调用curl_multi_add_handle
     * //这一步需要注意的是，curl_multi_add_handle的第二个参数是由curl_init而来的子handle。
     * //第三步：持续调用curl_multi_exec
     * //第四步：根据需要循环调用curl_multi_getcontent获取结果
     * //第五步：调用curl_multi_remove_handle，并为每个字handle调用curl_close
     * //第六步：调用curl_multi_close
     * //
     * //各函数作用解释：
     * //
     * //curl_multi_init()
     * //初始化一个curl批处理句柄资源。
     * //
     * //curl_multi_add_handle()
     * //向curl批处理会话中添加单独的curl句柄资源。curl_multi_add_handle()函数有两个参数，第一个参数表示一个curl批处理句柄资源，第二个参数表示一个单独的curl句柄资源。
     * //
     * //curl_multi_exec()
     * //解析一个curl批处理句柄，curl_multi_exec()函数有两个参数，第一个参数表示一个批处理句柄资源，第二个参数是一个引用值的参数，表示剩余需要处理的单个的curl句柄资源数量。
     * //
     * //curl_multi_remove_handle()
     * //移除curl批处理句柄资源中的某个句柄资源，curl_multi_remove_handle()函数有两个参数，第一个参数表示一个curl批处理句柄资源，第二个参数表示一个单独的curl句柄资源。
     * //
     * //curl_multi_close()
     * //关闭一个批处理句柄资源。
     * //
     * //curl_multi_getcontent()
     * //在设置了CURLOPT_RETURNTRANSFER的情况下，返回获取的输出的文本流。
     * //
     * //curl_multi_info_read()
     * //$arr=array(
     * //'www.baibu.com',
     * //'www.baibu.com',
     * //'www.baibu.com',
     * //'www.baibu.com',
     * //'www.baibu.com',
     * //'www.baibu.com',
     * //);
     * //async_get_url($arr);
     * ////var_dump(async_get_url($arr));
     * //echo '</br>';
     * //获取当前解析的curl的相关传输信息。
     */
    public static function async_get_url($array_url, $wait_usec = 0)
    {
        if (!is_array($array_url)) return false;
        $wait_usec = (int)$wait_usec;
        $data = array();
        $handle = array();
        $running = 0;
        $mh = curl_multi_init();//初始化一个curl批处理句柄资源。
        $i = 0;
        foreach ($array_url as $url) {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //  //不显示网页内容
            curl_setopt($ch, CURLOPT_TIMEOUT, 30);
            curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // 302 redirect
            curl_setopt($ch, CURLOPT_MAXREDIRS, 7);
            curl_multi_add_handle($mh, $ch);//把curl resource放到muilt_curl_handler里
            $handle[$i++] = $ch;
        }
        //执行
        do {
            curl_multi_exec($mh, $running);
            if ($wait_usec > 0) usleep($wait_usec);//每个connect需要间隔多久
        } while ($running > 0);

        //获取请求返回的数据
        foreach ($handle as $i => $ch) {
            $content = curl_multi_getcontent($ch);
            $data[$i] = (curl_errno($ch) == 0) ? $content : false;
        }
        //移除handle
        foreach ($handle as $ch) {
            curl_multi_remove_handle($mh, $ch);
        }
        //关闭批处理句柄
        curl_multi_close($mh);
        return $data;

    }

    /**
     * @param $url
     * @return bool|string
     * @User: yfl
     * @Time: 2017年9月26日 13:28:02
     * @description:PHP判断远程url是否有效
     */
    private static function checkUrlIsGet($url)
    {

        if (!function_exists('get_headers')) return '需要allow_url_fopen=on 开启';
        $array = get_headers($url, 1);
        if (preg_match('/200/', $array[0])) {
//            print_r($array);
            return true;
        } else {
            return false;
        }
    }
}